package io.gitee.minelx.filesystem;

import io.gitee.minelx.filesystem.init.OpenPolicy;
import io.gitee.minelx.filesystem.init.PathType;
import io.gitee.minelx.filesystem.provider.FS;
import io.gitee.minelx.filesystem.provider.PathMethodProvider;
import io.gitee.minelx.filesystem.values.PathDescription;

import java.util.Objects;

import static io.gitee.minelx.filesystem.init.OpenPolicy.EXISTS;

public class Path {

	private final FS fs;

	private final PathMethodProvider pathMethodProvider;

	private final PathDescription description;

	Path(FS fs,
		 PathMethodProvider pathMethodProvider,
		 PathDescription description) {
		this.fs = fs;
		this.pathMethodProvider = pathMethodProvider;
		this.description = description;
	}

	public String uri() {
		return pathMethodProvider.uri();
	}

	public boolean exists() {
		return pathMethodProvider.exists();
	}

	public String name() {
		return description.name();
	}

	public String absolutePath() {
		return description().toText();
	}

	PathDescription description() {
		return description;
	}

	public Path sibling(String name) {
		String siblingPath = description.sibling(name).toText();
		return create(fs, PathDescription.from(siblingPath));
	}

	public Directory container() {
		PathDescription destination = description.container();
		return Directory.create(create(fs, destination), OpenPolicy.EXISTS);
	}

	public Path rename(String name) {
		pathMethodProvider.rename(name);
		return create(fs, description.sibling(name));
	}

	public void delete() {
		if (!exists()) {
			throw new IllegalStateException("path at: '" + absolutePath() + "' is not existed, can not perform delete.");
		}
		determineType().delete(this);
	}

	public void deleteIfExists() {
		if (exists()) {
			delete();
		}
	}

	private PathType determineType() {
		if (!pathMethodProvider.exists()) {
			throw new FileAccessingException("error while determining path type at: '" + uri() + "' cause it's not existed.");
		}
		if (pathMethodProvider.isFile()) {
			return PathType.FILE;
		}
		return PathType.DIR;
	}

	public FS getPathMethodProviderFactory() {
		return fs;
	}

	public PathMethodProvider getPathMethodProvider() {
		return pathMethodProvider;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		Path path = (Path) o;
		return fs.equals(path.fs) && description.equals(path.description) && pathMethodProvider.equals(path.pathMethodProvider);
	}

	@Override
	public int hashCode() {
		return Objects.hash(fs, description, pathMethodProvider);
	}

	@Override
	public String toString() {
		return "Path{" +
				"fs=" + fs +
				", pathMethodProvider=" + pathMethodProvider +
				", description=" + description +
				'}';
	}

	public static Path create(FS fs, PathDescription description) {
		PathMethodProvider pathMethodProvider = fs.provider(description);
		return new Path(fs, pathMethodProvider, description);
	}
}
